/*
 *     This file is part of Mobile GPS Logger.
 *
 *     Mobile GPS Logger is free software: you can redistribute it and/or
 *     modify it under the terms of the GNU General Public License as
 *     published by the Free Software Foundation, either version 3 of the
 *     License, or (at your option) any later version.
 *
 *     Mobile GPS Logger is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License v3 for more details.
 *
 *     You should have received a copy of the GNU General Public License v3
 *     along with Mobile GPS Logger. If not, see <http://www.gnu.org/licenses/>.
 */

package cx.ath.skyflyer.gpslog;

import cx.ath.skyflyer.contactless.NdefClient;
import cx.ath.skyflyer.gpslog.ui.BusyAlert;
import java.io.IOException;
import java.util.Vector;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.List;
import javax.microedition.rms.*;

/**
 * This class handles the service discovery and displaying of these services.
 * @author Ahti Legonkov
 */
public class ServiceList implements DiscoveryListener, CommandListener {

    /**
     * Initialized the ServiceList object.
     * @param midlet a reference to the midlet that the list is used in.
     * @param prev the Displayable to navigate to when Back command is used
     *  on this screen.
     * @param device a reference to a discovered device that the user selected
     *  on the previous screen.
     */
    public ServiceList(GpsLogMidlet midlet, Displayable prev, RemoteDevice device) {
        m_Midlet = midlet;
        m_Device = device;
        m_Prev = prev;
    }

    private String getDeviceName() {
        try {
            return m_Device.getFriendlyName(false);
        }
        catch (IOException e) {
            return m_Device.getBluetoothAddress();
        }
    }
    /**
     * Start service discovery.
     */
    public void discover() {
        BusyAlert.show(m_Midlet, "Discovering services for " + getDeviceName());

        if (m_Services == null) {
            m_Services = new List("Discovered services", List.IMPLICIT);
            m_Services.addCommand(CMD_RETRY);
            m_Services.addCommand(CMD_BACK);
            m_Services.setCommandListener(this);
            m_ServiceVector = new Vector();
        }
        else {
            m_Services.deleteAll();
            m_ServiceVector.removeAllElements();
        }

        m_Services.removeCommand(CMD_SAVE);

        try {
            LocalDevice localDevice = LocalDevice.getLocalDevice();
            DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();
            UUID[] uuidSet = new UUID[1];
            // btspp services only
            uuidSet[0] = new UUID(0x1101);
            int[] attrSet = null;
            discoveryAgent.searchServices(attrSet, uuidSet, m_Device, this);
        }
        catch (BluetoothStateException ex) {
            displayAlert("Bluetooth error: " + ex.getMessage(), AlertType.ERROR);
        }
        catch (Exception ex) {
            displayAlert("Error: " + ex.getMessage(), AlertType.ERROR);
        }
    }

    //
    // Implementation of DiscoveryListener
    //

    public void deviceDiscovered(RemoteDevice d, DeviceClass c) {
        throw new UnsupportedOperationException("Not supported!");
    }

    public void inquiryCompleted(int i) {
        throw new UnsupportedOperationException("Not supported!");
    }

    public void serviceSearchCompleted(int arg0, int arg1) {
        Display disp = Display.getDisplay(m_Midlet);
        disp.setCurrent(m_Services);
        if (!m_ServiceVector.isEmpty()) {
            m_Services.addCommand(CMD_SAVE);
        }
    }

    public void servicesDiscovered(int transID, ServiceRecord[] sr) {
        for (int i = 0; i < sr.length; ++i) {
            m_ServiceVector.addElement(sr[i]);
            m_Services.append(serviceRecordToUrl(sr[i]), null);
        }
    }

    private static String serviceRecordToUrl(ServiceRecord sr) {
        return sr.getConnectionURL(ServiceRecord.AUTHENTICATE_NOENCRYPT, false);
    }

    //
    // Implementation of CommandListener
    //
    public void commandAction(Command c, Displayable d) {
        try {
            if (c == CMD_BACK) {
                onCmdBack();
            }
            else if (c == CMD_RETRY) {
                onCmdretry();
            }
            else if (c == CMD_SAVE) {
                onSaveAsDefault((List)d);
            }
            else if (c == List.SELECT_COMMAND && d == m_Services) {
                onListItemSelected((List)d);
            }
            else {
                // ... ?
            }
        }
        catch (Exception ex) {
            ex.printStackTrace();
            displayAlert(ex.toString(), AlertType.ERROR);
        }
    }

    //
    // Private parts...
    //

    private void onCmdBack() {
        Display display = Display.getDisplay(m_Midlet);
        display.setCurrent(m_Prev);
    }

    private void onCmdretry() {
        discover();
    }

    private void onSaveAsDefault(List list) {

        final Displayable prev = m_Midlet.getDisplay().getCurrent();

        // Ask the user where to save the service - to a TAG or to phone.
        final Alert a = new Alert("Save url");
        a.setString("Where do you want to save the URI?");
        final Command cmdTag = new Command("Tag", Command.SCREEN, 1);
        final Command cmdPhone = new Command("Phone", Command.SCREEN, 1);
        final Command cmdCancel = new Command("Cancel", Command.CANCEL, 1);
        if (NdefClient.useNfc())
            a.addCommand(cmdTag);
        a.addCommand(cmdPhone);
        a.addCommand(cmdCancel);

        ServiceRecord sr = (ServiceRecord)m_ServiceVector.elementAt(list.getSelectedIndex());
        final String url = serviceRecordToUrl(sr);

        CommandListener commandListener = new CommandListener() {

            public void commandAction(Command c, Displayable d) {
                if (c == cmdTag) {
                    //assert(NdefClient.useNfc());
                    if (NdefClient.useNfc())
                        saveToTag(url);
                }
                else if (c == cmdPhone) {
                    saveToRecordStore(url);
                }
                else /*if (c == cmdCancel)*/ {
                    m_Midlet.getDisplay().setCurrent(prev);
                }
            }
        };

        a.setCommandListener(commandListener);

        Display.getDisplay(m_Midlet).setCurrent(a, prev);
        
        // if (must save to record store)
        //displayAlert("Saving url:" + url, AlertType.INFO);
            
        // else if (must save to NFC tag)
            // saveToTag(url);
        
        // else ...
    }

    private void saveToRecordStore(String url) {
        try {
            RecordStore recordStore = RecordStore.openRecordStore("GpsLoggerStore", true);
            byte[] data = url.getBytes();

            final int DEFAULT_URL_RECORD_INDEX = 1;
            final int DEFAULT_NAME_RECORD_INDEX = 2;

            try {
                recordStore.setRecord(DEFAULT_URL_RECORD_INDEX, data, 0, data.length);
                displayAlert("Record changed", AlertType.INFO);
            }
            catch (InvalidRecordIDException ex) {
                int idx = recordStore.addRecord(data, 0, data.length);
                displayAlert("Record added: "+idx, AlertType.INFO);
            }

            byte[] name = "Unknown".getBytes();
            try {
                name = this.m_Device.getFriendlyName(true).getBytes();
            }
            catch (Exception ex) { }

            try {
                recordStore.setRecord(DEFAULT_NAME_RECORD_INDEX, name, 0, name.length);
            }
            catch (InvalidRecordIDException ex) {
                int idx = recordStore.addRecord(name, 0, name.length);
            }

            recordStore.closeRecordStore();
        }
        catch (RecordStoreException ex) {
            displayAlert("Failed to save: "+ex, AlertType.ERROR);
        }
    }

    private void saveToTag(String url) {
        displayAlert("TODO: Save to Tag", AlertType.INFO);
    }

    private void onListItemSelected(List list) {
        if (list == null) {
            displayAlert("list is null", AlertType.ERROR);
            return;
        }

        int serviceIndex = list.getSelectedIndex();
        if (serviceIndex < 0 || serviceIndex >= m_ServiceVector.size()) {
            displayAlert("bad index", AlertType.ERROR);
            return;
        }

        ServiceRecord sr = (ServiceRecord)m_ServiceVector.elementAt(serviceIndex);
        if (sr == null) {
            displayAlert("Service record is null.", AlertType.ERROR);
            return;
        }

        String url = sr.getConnectionURL(ServiceRecord.AUTHENTICATE_NOENCRYPT, false);
        if (url == null) {
            displayAlert("URL is null.", AlertType.ERROR);
            return;
        }

        Logger log = new Logger(m_Midlet, list, url);
    }

    private void displayAlert(String msg, AlertType type) {
        //assert(m_Midlet != null);

        Display display = Display.getDisplay(m_Midlet);
        Alert a = new Alert("Error", msg, null, type);
        a.setTimeout(2000);
        display.setCurrent(a, m_Services);
    }

    /** A Command to retry scanning for services. */
    private static final Command CMD_RETRY = new Command("Retry", Command.SCREEN, 1);

    /** A Command to go back to the previous page. */
    private static final Command CMD_BACK = new Command("Back", Command.BACK, 2);

    /** A Command to save the selected service URL. */
    private static final Command CMD_SAVE = new Command("Save", Command.SCREEN, 1);

    private RemoteDevice m_Device;
    private GpsLogMidlet m_Midlet;
    private List m_Services;
    private Vector m_ServiceVector;
    private Displayable m_Prev;
}
